package com.tengits.flutter_tcos;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;


import com.tencent.cos.xml.CosXmlServiceConfig;
import com.tencent.cos.xml.CosXmlSimpleService;
import com.tencent.cos.xml.exception.CosXmlClientException;
import com.tencent.cos.xml.exception.CosXmlServiceException;
import com.tencent.cos.xml.listener.CosXmlResultListener;
import com.tencent.cos.xml.model.CosXmlRequest;
import com.tencent.cos.xml.model.CosXmlResult;
import com.tencent.cos.xml.transfer.COSXMLUploadTask;
import com.tencent.cos.xml.transfer.TransferConfig;
import com.tencent.cos.xml.transfer.TransferManager;
import com.tencent.qcloud.core.auth.QCloudCredentialProvider;
import com.tencent.qcloud.core.auth.SessionCredentialProvider;
import com.tencent.qcloud.core.http.HttpRequest;
import com.tencent.qcloud.core.http.RequestBodySerializer;

import org.json.JSONObject;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/** FlutterTcosPlugin */
public class FlutterTcosPlugin implements FlutterPlugin, MethodCallHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;

  private Context context;
  private TransferManager mTransferManager;
  private String mAppID;
  private String mBucket;
  private int mSystemId;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_tcos");
    channel.setMethodCallHandler(this);
    context = flutterPluginBinding.getApplicationContext();
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else if(call.method.equals("initCosServer")) {
      initCosServer(call, result);
    } else if (call.method.equals("upload")) {
      upload(call, result);
    } else if (call.method.equals("uploadWithName")) {
      uploadWithName(call, result);
    } else {
      result.notImplemented();
    }
  }

  private void initCosServer(@NonNull MethodCall call, @NonNull Result result) {
    mAppID = call.argument("appID");
    mBucket = call.argument("bucket");
    mSystemId = call.argument("systemId");
    String region = call.argument("region");
    String signUrl = call.argument("signUrl");
    String cookie = call.argument("cookie");
    CosXmlServiceConfig cosXmlServiceConfig = new CosXmlServiceConfig.Builder()
            .isHttps(false)
            .setRegion(region)
            .setDebuggable(true)
            .builder();

    URL url = null;

    try {
      url = new URL(signUrl);
    } catch (MalformedURLException e) {
      result.error("exception", "signurl exception", null);
    }

    /**
     * 初始化 {@link QCloudCredentialProvider} 对象，来给 SDK 提供临时密钥。
     */
    Map<String,String> entity = new HashMap<>();
    entity.put("bucket",mBucket+"-"+mAppID);
    entity.put("region",region);
    if(mSystemId > 0)
    {
      entity.put("systemId", ""+mSystemId);
    }
    JSONObject json =new JSONObject(entity);
    HttpRequest.Builder<String> build = new HttpRequest.Builder<String>();
    build.url(url).method("POST").body(RequestBodySerializer.string("application/json; charset=utf-8",json.toString()));
    if(!TextUtils.isEmpty(cookie)) {
      build.addHeader("authorization",cookie);
    }
    QCloudCredentialProvider credentialProvider = new SessionCredentialProvider(build.build());

    CosXmlSimpleService cosXmlService = new CosXmlSimpleService(this.context, cosXmlServiceConfig, credentialProvider);

    TransferConfig transferConfig = new TransferConfig.Builder()
            .setDividsionForCopy(5 * 1024 * 1024) // 是否启用分块复制的最小对象大小
            .setSliceSizeForCopy(5 * 1024 * 1024) // 分块复制时的分块大小
            .setDivisionForUpload(2 * 1024 * 1024) // 是否启用分块上传的最小对象大小
            .setSliceSizeForUpload(1024 * 1024) // 分块上传时的分块大小
            .build();
    mTransferManager = new TransferManager(cosXmlService, transferConfig);

    result.success(1);
  }

  private void uploadWithName(@NonNull final MethodCall call, @NonNull final Result promise) {

    if(mTransferManager == null) {
      promise.error("not init","not init", null);
      return;
    }

    String originLocalPath = call.argument("originLocalPath");
    String targetPath = call.argument("targetPath");

    String cosPath = targetPath;

    String localPath = originLocalPath.replace("file://", "");

    Log.e("cos", "target:" + cosPath + "|local:" + localPath);

    // 开始上传，并返回生成的 COSXMLUploadTask
    COSXMLUploadTask cosxmlUploadTask = mTransferManager.upload(mBucket+"-"+mAppID, cosPath,
            localPath, null);

    // 设置结果监听
    cosxmlUploadTask.setCosXmlResultListener(new CosXmlResultListener() {
      @Override
      public void onSuccess(final CosXmlRequest request, final CosXmlResult ret) {
        Log.e("cos", "success" + ret.accessUrl);
        new Handler(Looper.getMainLooper()).post(new Runnable() {
          @Override
          public void run() {
            // Call the desired channel message here.
            promise.success(ret.accessUrl);
          }
        });
      }

      @Override
      public void onFail(final CosXmlRequest request, final CosXmlClientException exception, final CosXmlServiceException serviceException) {
        // todo Upload failed because of CosXmlClientException or CosXmlServiceException...
        Log.e("cos", "exception");
        new Handler(Looper.getMainLooper()).post(new Runnable() {
          @Override
          public void run() {
            // Call the desired channel message here.
            promise.error(serviceException != null?serviceException.getLocalizedMessage():"server exception empty",
                    exception != null ? exception.getLocalizedMessage():"client exception null",
                    request.getRequestURL());
          }
        });
      }
    });
  }

  private void upload(@NonNull final MethodCall call, @NonNull final Result promise) {

    if(mTransferManager == null) {
      promise.error("not init","not init", null);
      return;
    }

    String originLocalPath = call.argument("originLocalPath");
    String targetPath = call.argument("targetPath");

    String cosPath = targetPath + String.valueOf(System.currentTimeMillis()/1000) + new File(originLocalPath).getName();

    String localPath = originLocalPath.replace("file://", "");

    Log.e("cos", "target:" + cosPath + "|local:" + localPath);

    // 开始上传，并返回生成的 COSXMLUploadTask
    COSXMLUploadTask cosxmlUploadTask = mTransferManager.upload(mBucket+"-"+mAppID, cosPath,
            localPath, null);

    // 设置结果监听
    cosxmlUploadTask.setCosXmlResultListener(new CosXmlResultListener() {
      @Override
      public void onSuccess(final CosXmlRequest request, final CosXmlResult ret) {
        Log.e("cos", "success" + ret.accessUrl);
        new Handler(Looper.getMainLooper()).post(new Runnable() {
          @Override
          public void run() {
            // Call the desired channel message here.
            promise.success(ret.accessUrl);
          }
        });
      }

      @Override
      public void onFail(final CosXmlRequest request, final CosXmlClientException exception, final CosXmlServiceException serviceException) {
        // todo Upload failed because of CosXmlClientException or CosXmlServiceException...
        Log.e("cos", "exception");
        new Handler(Looper.getMainLooper()).post(new Runnable() {
          @Override
          public void run() {
            // Call the desired channel message here.
            promise.error(serviceException != null?serviceException.getLocalizedMessage():"server exception empty",
                    exception != null ? exception.getLocalizedMessage():"client exception null",
                    request.getRequestURL());
          }
        });
      }
    });
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
  }
}
